package com.jiawa.train.business.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jiawa.train.business.domain.*;
import com.jiawa.train.business.dto.ConfirmOrderMQDto;
import com.jiawa.train.business.enums.ConfirmOrderStatusEnum;
import com.jiawa.train.business.enums.RedisKeyPreEnum;
import com.jiawa.train.business.enums.SeatColEnum;
import com.jiawa.train.business.enums.SeatTypeEnum;
import com.jiawa.train.business.mapper.ConfirmOrderMapper;
import com.jiawa.train.business.req.ConfirmOrderDoReq;
import com.jiawa.train.business.req.ConfirmOrderQueryReq;
import com.jiawa.train.business.req.ConfirmOrderTicketReq;
import com.jiawa.train.common.exception.BusinessException;
import com.jiawa.train.common.exception.BusinessExceptionEnum;
import com.jiawa.train.common.resp.PageResult;
import com.jiawa.train.common.util.SnowUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class ConfirmOrderService {
    //reource是使用名称注入的方式而autowired是使用类型注入的方式
    @Resource
    private ConfirmOrderMapper confirmOrderMapper;

    @Resource
    private DailyTrainTicketService dailyTrainTicketService;

    @Resource
    private DailyTrainCarriageService dailyTrainCarriageService;

    @Resource
    private DailyTrainSeatService dailyTrainSeatService;

    @Resource
    private AfterConfirmOrderService afterConfirmOrderService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private SkTokenService skTokenService;


    //新增或者更新用户信息，通过判断是否有id决定进行新增操作还是更新操作
    public void save(ConfirmOrderDoReq req) {
        //将传递的参数赋给ConfirmOrder对象,在req中没有id,createTime,updateTime的数据，需要自己进行赋值
        //这里的id使用雪花算法，然后创建时间使用当前时间
        DateTime now = DateTime.now();
        ConfirmOrder confirmOrder = BeanUtil.copyProperties(req, ConfirmOrder.class);
        //如果id为空，则进行新增操作
        if (ObjectUtil.isNull(confirmOrder.getId())) {
            confirmOrder.setId(SnowUtil.getSnowflakeNextId());
            confirmOrder.setCreateTime(now);
            confirmOrder.setUpdateTime(now);
            confirmOrderMapper.insert(confirmOrder);
        }
        //否则进行更新操作
        else {
            confirmOrder.setUpdateTime(now);
            confirmOrderMapper.updateByPrimaryKeySelective(confirmOrder);
        }
    }

    //如果用户id存在则进行根据id进行查询乘客的信息，如果是用户id不存在则进行查询所有,全部都按分页进行处理
    public PageResult queryConfirmOrderList(ConfirmOrderQueryReq req) {
        //创建confirmOrderExample对象
        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
        confirmOrderExample.setOrderByClause("id desc");
        //设置查询条件
        ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();

        // 先设置分页的参数
        PageHelper.startPage(req.getPage(), req.getPageSize());
        //进行查询数据库
        List<ConfirmOrder> confirmOrderList = confirmOrderMapper.selectByExample(confirmOrderExample);
        //返回查询结果,转为分页的形式,
        PageInfo<ConfirmOrder> pageInfo = new PageInfo<>(confirmOrderList);
        //获取数据进行封装
        PageResult pageResult = new PageResult(pageInfo.getTotal(), pageInfo.getList());
        //返回查询结果
        return pageResult;
    }

    public void delete(Long id) {
        confirmOrderMapper.deleteByPrimaryKey(id);
    }

   //核心的业务逻辑，然后要在使用redission分布式锁保证互斥访问资源，
   @Async
   public void doConfirmOrder(ConfirmOrderMQDto confirmOrderMQDto) {
        MDC.put("LOG_ID",confirmOrderMQDto.getLogId());
       //1.进行获取锁资源
       String lockKey = RedisKeyPreEnum.CONFIRM_ORDER + "-" + confirmOrderMQDto.getDate() + "-" + confirmOrderMQDto.getTrainCode();
       RLock lock = null;
       lock = redissonClient.getLock(lockKey);
       try {
           boolean tryLock = lock.tryLock(0, TimeUnit.SECONDS);
           if (tryLock) {
               log.info("恭喜，抢到购票锁了！");
           } else {
               log.info("很遗憾，没有抢到锁，有其它车次线程正在出票！");
               return;
           }
            //2.进行排队购票的操作,抢到同一车次的订单进行处理
            while(true){
                //2.1查询初始化该车次的订单列表
                List<ConfirmOrder> confirmOrderList = queryInitConfirmOrderList(confirmOrderMQDto);
                if(CollUtil.isEmpty(confirmOrderList)){
                    log.info("没有需要处理的订单，结束循环！");
                    break;
                }else{
                    log.info("本次需要处理的订单数量为：{}",confirmOrderList.size());
                }
                for(ConfirmOrder confirmOrder : confirmOrderList){
                    //2.2 将订单状态改为处理中,然后调用函数进行处理
                    confirmOrder.setStatus(ConfirmOrderStatusEnum.PENDING.getCode());
                    updateConfirmOrderStatus(confirmOrder);
                    try{
                        sell(confirmOrder);
                    }catch(BusinessException e){
                        //当批量购票的时候如果一等座或者二等座的票数不够则更新订单状态由处理中转为失败，进行下一个订单的处理，而不是抛出异常
                        if(e.getE().equals(BusinessExceptionEnum.CONFIRM_ORDER_YDZ_TICKET_COUNT_ERROR) || e.getE().equals(BusinessExceptionEnum.CONFIRM_ORDER_EDZ_TICKET_COUNT_ERROR)){
                            log.info("本订单余票不足，将订单状态改为失败继续售卖下一个订单!");
                            confirmOrder.setStatus(ConfirmOrderStatusEnum.EMPTY.getCode());
                            updateConfirmOrderStatus(confirmOrder);
                            continue;
                        }else{
                            throw e;
                        }
                    }

                }
            }

       } catch (InterruptedException | ParseException e){
        log.error("获取购票锁资源异常！");
    }finally {
        if(lock != null && lock.isLocked() && lock.isHeldByCurrentThread()){
            log.info("释放购票锁！");
            lock.unlock();
        }
    }
   }

    //1.进行单个初始化相同车次的购票
    public void sell(ConfirmOrder confirmOrder) {
        // 为了演示排队效果，每次出票增加200毫秒延时
//        try {
//            Thread.sleep(200);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        //1.构造ConfirmOrderDoReq因为后续的操作还是要使用req而不是confirmOrderMQDto（这个传递的参数不足）
        ConfirmOrderDoReq req = new ConfirmOrderDoReq();
        req.setId(confirmOrder.getId());
        req.setMemberId(confirmOrder.getMemberId());
        req.setDate(confirmOrder.getDate());
        req.setTrainCode(confirmOrder.getTrainCode());
        req.setStart(confirmOrder.getStart());
        req.setEnd(confirmOrder.getEnd());
        req.setDailyTrainTicketId(confirmOrder.getDailyTrainTicketId());
        req.setStatus(confirmOrder.getStatus());
        req.setCreateTime(confirmOrder.getCreateTime());
        req.setUpdateTime(confirmOrder.getUpdateTime());
        req.setTickets(JSON.parseArray(confirmOrder.getTickets(), ConfirmOrderTicketReq.class));
        req.setImageCode("");
        req.setImageCodeToken("");
        //2.使用余票id进行查找每日座位的信息，从而获得余票数
        Date date = confirmOrder.getDate();
        String trainCode = confirmOrder.getTrainCode();
        String start = confirmOrder.getStart();
        String end = confirmOrder.getEnd();
        List<DailyTrainSeat> finalSeatList = new ArrayList<>();
        DailyTrainTicket dailyTrainTicket = dailyTrainTicketService.getDailyTrainTicket(date, trainCode, start, end);
        log.info("查询到的余票信息为：{}", dailyTrainTicket);
        Byte startIndex = dailyTrainTicket.getStartIndex();
        Byte endIndex = dailyTrainTicket.getEndIndex();
        //3.扣减余票数量，并判断余票是否足够
        reduceDailyTrainTicket(dailyTrainTicket, req);
        //4.选座
        List<ConfirmOrderTicketReq> tickets = req.getTickets();
        ConfirmOrderTicketReq ticket = tickets.get(0);
        //4.1 首先进行条件判断，用户是否进行选座，如果进行选座，则计算偏移值
        if (StrUtil.isNotBlank(ticket.getSeat())) {
            log.info("本次购票有选座，需要计算偏移值");
            //4.1.1 根据用户选择的座位类型，进行获取用户的座位都有什么，比如一等座就是{ACDF},二等座就是{ABCDF}
            List<SeatColEnum> seatColEnum = SeatColEnum.getColsByType(ticket.getSeatTypeCode());
            //4.1.2 创建一个新的数组，然后将数据变成和前端一样两排的形式，将其压入数组中
            List<String> referSeatList = new ArrayList<>();
            for (int i = 1; i <= 2; i++) {
                for (SeatColEnum seatColEnum1 : seatColEnum) {
                    referSeatList.add(seatColEnum1.getCode() + i);
                }
            }
            //4.1.3 遍历用户的座位信息和数组中的数据进行比对，然后将其索引压入数组中，获取绝对偏移值
            List<Integer> aboluteOffsetList = new ArrayList<>();
            for (ConfirmOrderTicketReq ticket1 : tickets) {
                //直接获取值的索引
                int index = referSeatList.indexOf(ticket1.getSeat());
                aboluteOffsetList.add(index);
            }
            //4.1.4 遍历绝对偏移值，将每一个数据都减去第一个索引，获取相对偏移值
            List<Integer> relativeOffsetList = new ArrayList<>();
            for (Integer index : aboluteOffsetList) {
                relativeOffsetList.add(index - aboluteOffsetList.get(0));
            }
            log.info("相对偏移值列表：{}", relativeOffsetList);
            //4.1.5 查询购票信息，进行选座
            genSeat(
                    finalSeatList,
                    date,
                    trainCode,
                    ticket.getSeatTypeCode(),
                    startIndex,
                    endIndex,
                    ticket.getSeat().split("")[0],
                    relativeOffsetList
            );
        } else {
            log.info("本次购票没有选座，不需要计算偏移值");
            for (ConfirmOrderTicketReq ticket1 : tickets) {
                genSeat(
                        finalSeatList,
                        date,
                        trainCode,
                        ticket1.getSeatTypeCode(),
                        startIndex,
                        endIndex,
                        null,
                        null
                );
            }
        }
        log.info("最终座位列表：{}", finalSeatList);
        //6.选中座位后事务处理
        try {
            afterConfirmOrderService.afterConfirmOrder(dailyTrainTicket, finalSeatList, tickets, confirmOrder);
        } catch (Exception e) {
            log.error("保存购票信息失败", e);
            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_EXCEPTION);
        }
    }
    //根据获取到的每日车票的信息，进行判断用户是购买的一等座还是二等座然后对应的进行扣减库存数量然后进行判断，
    //2.功能：进行扣减库存数量，以及库存数量不够的异常处理
    public void reduceDailyTrainTicket(DailyTrainTicket dailyTrainTicket, ConfirmOrderDoReq req) {
        //遍历获取到的购票的信息，获取用户选择的座位类型，然后针对不同的座位类型进行扣减库存数量
        for(ConfirmOrderTicketReq ticketReq : req.getTickets()){
            String seatTypeCode = ticketReq.getSeatTypeCode();
            //当seatTypeCode为1时，返回的是一个枚举实例YDZ
            SeatTypeEnum seatTypeEnum = EnumUtil.getBy(SeatTypeEnum::getCode,seatTypeCode);
            switch (seatTypeEnum){
                //当为一等座时
                case YDZ:
                    int countLeft = dailyTrainTicket.getYdz() - 1;
                    if(countLeft < 0){
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_YDZ_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYdz(countLeft);
                //当为二等座时
                case EDZ:
                    int countLeft1 = dailyTrainTicket.getEdz() - 1;
                    if(countLeft1 < 0){
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_EDZ_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setEdz(countLeft1);
            }
        }
        //Todo:问题是在余票数量足够的情况下，重新赋值不同座位的剩余数量，但是还没有修改数据库中的数据，是不是分配完座位之后才进行修改数据库中的数据，是的
    }

    //3.根据日期，车次，座位类型，先查找到车厢信息，然后在根据上述的信息查找到座位信息进行返回，然后进行选座，返会选座的结果
    public void genSeat(List<DailyTrainSeat> finalSeatList,Date date, String trainCode, String seatTypeCode, Byte startIndex, Byte endIndex, String column, List<Integer> relativeOffsetList) {
        //用户选中的座位
        List<DailyTrainSeat> getSeatList = new ArrayList<>();
        //1.根据日期、车次、座位类型进行查询车厢
        List<DailyTrainCarriage> Carriages = dailyTrainCarriageService.selectCarriagesBySeatType(date, trainCode, seatTypeCode);
        log.info("车厢列表：{}", Carriages);
        getSeatList = new ArrayList<>();
        //2.根据日期、车次、车厢索引、座位类型进行查询座位
        for (DailyTrainCarriage carriage : Carriages) {
            List<DailyTrainSeat> seats = dailyTrainSeatService.selectSeatsByCarriageIndexAndSeatType(date, trainCode, carriage.getIndex(), seatTypeCode);
            log.info("座位列表：{}", seats);
            //3.遍历座位信息
            for (DailyTrainSeat seat : seats) {
                //3.1 获取座位的索引
                Integer index = seat.getCarriageSeatIndex();
                //3.2 获取座位的列是A,C,D,F中的哪一个
                String col = seat.getCol();
                //当不支持选座的时候，避免一个座位被多次选择，所以进行判断当前座位是否已被选中,这里不能使用局部变量getSeatList，因为当函数结束的时候，变量会被清空，但是全局变量不会被清空
                boolean alreadyChooseFlag = false;
                for(DailyTrainSeat finalSeat : finalSeatList){
                    //如果当前座位的索引和getSeat的索引一致，则说明当前座位已被选中，跳出这个
                    if(finalSeat.getCarriageSeatIndex().equals(seat.getCarriageSeatIndex())){
                        alreadyChooseFlag = true;
                        break;
                    }
                }
                //结束此次的循环，进行下次的循环
                if(alreadyChooseFlag){
                    continue;
                }
                //3.3 根据column来进行判断用户是否选座,当用户进行选座的时候则需要匹配用户选择的列名和当前的列名是否一致
                if(StrUtil.isNotBlank(column)){
                    //3.4 当用户选择的列名和当前的列名不一致的时候，则结束本次的循环，进行遍历下一个座位，直到找到一个相同列名的座位
                    if(!col.equals(column)){
                        continue;
                    }
                }else{
                    log.info("没有选座");
                }
                //3.5 找到相同列名的座位后进行判断该座位是否可以购买
                Boolean isChoose = calSeat(seat, startIndex, endIndex);
                //3.6 若该座位可以选择就进行根据偏移值获取下一个坐标的位置，然后进行判断下一个坐标是否可以选择
                if (isChoose) {
                    log.info("找到一个合适的座位");
                    getSeatList.add(seat);
                }
                //3.7 如果没有找到合适的，则结束本次循环找到合适的座位
                else{
                    log.info("没有找到合适的座位");
                    continue;
                }
                boolean isGetAllOffsetSeat = true;
                //3.8 根据上述的操作，无论是用户自己购买座位类型的还是不支持选座的都找到第一个合适的座位了。
                //如果不支持选座，则根据用户购票信息进行遍历，得到可以被选座的座位（2个/多个）
                //如果是用户自己购买的座位类型，就根据relativeOffsetList进行得出下一个坐标，然后进行判断该坐标是否可以选择
                //首先：判断数组是否为空，若不为空则表示有其他的坐标需要获取
                if(CollUtil.isNotEmpty(relativeOffsetList)){
                    //3.9 遍历偏移值数组中的数据,第0个就是用户刚才已经选中的座位，接下来就是获取下一个坐标
                    for(int i = 1; i < relativeOffsetList.size(); i++){
                        //3.10 因为索引是从0开始的，所以需要减1，我一开始获取的值是从1开始的，所以需要减1，才能得到正确的索引
                        Integer nextIndex = index + relativeOffsetList.get(i) - 1;
                        //3.11 有选座时，要保证选择的是同一个车厢，不能进行跨车厢选择
                        if(nextIndex >= seats.size()){
                            log.info("座位{}不可选,偏移后的索引超出了这个车厢的座位数",nextIndex);
                            isGetAllOffsetSeat = false;
                            break;
                        }
                        //3.12 根据索引获取下一个座位的信息
                        DailyTrainSeat nextDailyTrainSeat = seats.get(nextIndex);
                        Boolean isNextChoose = calSeat(nextDailyTrainSeat, startIndex, endIndex);
                        //3.13 如果该座位可以选择则继续进行，否则直接结束大的循环，因为第一个选中的座位也不能使用了，要重新在寻找第一个合适的座位
                        if(isNextChoose){
                            log.info("座位{}被选中", nextDailyTrainSeat.getCarriageSeatIndex());
                            getSeatList.add(nextDailyTrainSeat);
                        }else{
                            log.info("座位{}不可选", nextDailyTrainSeat.getCarriageSeatIndex());
                            //而这个break是指跳出本次的循环，但是这里是需要跳出大的循环重新开始寻找一个合适的座位
                            isGetAllOffsetSeat = false;
                            break;
                        }
                    }
                }
                //3.14若只能选中第一个座位，后面的根据偏移值获取的座位不能选中，则结束本次的循环，从新选择第一个合适的座位
                if(!isGetAllOffsetSeat){
                    getSeatList = new ArrayList<>();
                    continue;
                }
                //3.15 若所有的座位都可以被选中，该方法就结束了，
                finalSeatList.addAll(getSeatList);
                return;
            }
        }
    }
    //计算某座位在区间内是否可卖，例：sell=10001,本次购买区间站1-4，则区间已售000，全部为0则表示该区间可卖，只要有1，就表示该区间不可买
    //选中后，要先计算购票后的sell,比如原来是10001，本次购买区间站1-4，方案：构造本次购票造成的售票信息01110，和原来sell10001进行按位或，最终得到11111
    //4.功能：进行单个座位的选择，该座位是否可以购买
    public boolean calSeat(DailyTrainSeat seat, Byte startIndex, Byte endIndex) {
        //获取座位信息
        String sell = seat.getSell();
        log.info("原始的座位信息：{}", sell);
        //现实中举例：0000，我想要购买1-3的票，先截取中这一段的字符串000，Todo:站序从0开始
        String subString = sell.substring(startIndex, endIndex);
        log.info("截取的座位信息：{}", subString);
        //然后将二进制字符串转为整数进行判断
        int integer = NumberUtil.binaryToInt(subString);
        if (integer == 0) {
            log.info("该座位可以买！");
            //将该区间的数据转为111,使用字符串进行替换
            String curSell = subString.replace('0', '1');
            //未购买的前面和后面进行补零，保证二进制字符串的长度不变
            curSell = StrUtil.fillBefore(curSell, '0', endIndex);
            //在补零后得到01110
            curSell = StrUtil.fillAfter(curSell, '0', sell.length());
            //将新获取的座位信息和之前购买过的座位信息进行按位或操作，
            // 使用NumberUtil工具类进行转换为数值，在计算机中以二进制的方式进行存储，所以可以进行按位或操作,
            //产生的结果为14，因为按位或操作是计算机执行的，然后显示还是十进制数据
            int newSellInt = NumberUtil.binaryToInt(curSell) | NumberUtil.binaryToInt(sell);
            //将结果转为二进制字符串
            String newSell = NumberUtil.getBinaryStr(newSellInt);
            newSell = StrUtil.fillBefore(newSell, '0', sell.length());
            log.info("座位{}被选中，原售票信息：{}，车站区间：{}~{}，即：{}，最终售票信息：{}"
                    , seat.getCarriageSeatIndex(), sell, startIndex, endIndex, curSell, newSell);
            seat.setSell(newSell);
            return true;
        } else {
            log.info("该座位不可买！");
            return false;
        }
    }

    //5.查询订单信息
    public List<ConfirmOrder> queryInitConfirmOrderList(ConfirmOrderMQDto confirmOrderMQDto) throws ParseException {
        log.info("传递的参数：{}", confirmOrderMQDto);
        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
        confirmOrderExample.setOrderByClause("id asc");
        ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();
        criteria.andDateEqualTo(confirmOrderMQDto.getDate());
        criteria.andTrainCodeEqualTo(confirmOrderMQDto.getTrainCode());
        criteria.andStatusEqualTo(ConfirmOrderStatusEnum.INIT.getCode());
        PageHelper.startPage(1, 5);
        List<ConfirmOrder> confirmOrderList = confirmOrderMapper.selectByExample(confirmOrderExample);
        return confirmOrderList;
    }

    //6.更新订单状态
    public void updateConfirmOrderStatus(ConfirmOrder confirmOrder) {
        DateTime now = DateTime.now();
        log.info("更新订单状态：{}", confirmOrder);
        ConfirmOrder confirmOrderForUpdate = new ConfirmOrder();
        confirmOrderForUpdate.setId(confirmOrder.getId());
        confirmOrderForUpdate.setStatus(confirmOrder.getStatus());
        confirmOrderForUpdate.setUpdateTime(now);
        confirmOrderMapper.updateByPrimaryKeySelective(confirmOrder);
    }


    //7、根据订单的id查询订单的状态，根据不同的订单状态进行返回数据
    public Integer queryLineCount(Long id) {
        ConfirmOrder confirmOrder = confirmOrderMapper.selectByPrimaryKey(id);
        ConfirmOrderStatusEnum statusEnum = EnumUtil.getBy(ConfirmOrderStatusEnum::getCode, confirmOrder.getStatus());
        int result = switch (statusEnum){
            case PENDING -> 0; // 排队0
            case SUCCESS -> -1; // 成功
            case FAILURE -> -2; // 失败
            case EMPTY -> -3; // 无票
            case CANCEL -> -4; // 取消
            case INIT -> 999; // 需要查表得到实际排队数量
        };
        //如果返回的是999则需要进行查一下排队的人数
        if(result == 999){
            // 排在第几位，下面的写法：where a=1 and (b=1 or c=1) 等价于 where (a=1 and b=1) or (a=1 and c=1)
            //计算出创建时间比该订单创建时间早并且订单状态为初始化的订单和创建时间比该订单创建时间早并且订单状态为处理中的订单
            ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
            confirmOrderExample.or().andDateEqualTo(confirmOrder.getDate())
                    .andTrainCodeEqualTo(confirmOrder.getTrainCode())
                    .andCreateTimeLessThan(confirmOrder.getCreateTime())
                    .andStatusEqualTo(ConfirmOrderStatusEnum.INIT.getCode());
            confirmOrderExample.or().andDateEqualTo(confirmOrder.getDate())
                    .andTrainCodeEqualTo(confirmOrder.getTrainCode())
                    .andCreateTimeLessThan(confirmOrder.getCreateTime())
                    .andStatusEqualTo(ConfirmOrderStatusEnum.PENDING.getCode());
            //查询数据库中符合上述条件的订单数，一开始为long型，在不超过的范围内将其转为int型
            return Math.toIntExact(confirmOrderMapper.countByExample(confirmOrderExample));
        }else{
            return result;
        }
    }
}